##============================================================================##
## You need the following if this is ever being run as a script.
import os, sys  
sys.path.insert(0, "C:\Documents and Settings\wendy_admin\src\eclipse_workspace\djangodb\main")  
if not os.environ.has_key("DJANGO_SETTINGS_MODULE"):
    os.environ["DJANGO_SETTINGS_MODULE"]="wjContact.settings"
##============================================================================##

    
import datetime
import pdb

from django import newforms as forms
from django.newforms.forms import SortedDictFromList

## This is for the check_for_duplicates method. Should consider refactoring
## this, to keep views type stuff out of this forms file.
from django.shortcuts import render_to_response

from wjContact.constants import MSG_CODES, DEFAULT_LISTSIZE

## Core cbv field-related stuff
from wjContact.views.fields import FieldList 



    
        
class DeclarativeExtensibleFormMetaclass(type):
    """
    Metaclass that converts Field attributes to a dictionary called
    'base_fields', taking into account parent class 'base_fields' as well.
    """
    #from forms.fields import Field
    #from django.newforms.fields import Field
    
    def __new__(cls, name, bases, attrs):
        Field = forms.Field
        ## field_series will hold a list of 
        ## (name, recurrent_field_array) pairs
        fieldlists = [
                (key, attrs.pop(key)) for key, obj in attrs.items() 
                if isinstance(obj, FieldList)
                ]
        ## We need klass to be the class of the 'main' object that this form is 
        ## all about - ie a Contact or a Recipient or whatever.
        ## There had betber only be one of these - kludgey!   XXX FIXME
#        klasses = [
#                (key, attrs.pop(key)) for key, obj in attrs.items() 
#                if (type(obj) == 'type')
#                ]
#        attrs['klass'] = klasses[0]
        ## Put fieldlists into attrs so that it will be available to 
        ## instances
        attrs['fieldlists'] = fieldlists
        for (fieldlist_name, fieldlist) in fieldlists:
            ## next line sets up 'form.pets=RecurringField(whatever)'
            attrs[fieldlist_name] = fieldlist
           
        fields = [(field_name, attrs.pop(field_name)) for field_name, obj in attrs.items() if isinstance(obj, Field)]

        ## May need think about the creation counter in the copies at some 
        ## point (see definiton of FieldList).
        fields.sort(lambda x, y: cmp(x[1].creation_counter, y[1].creation_counter))
        
        # If this class is subclassing another Form, add that Form's fields.
        # Note that we loop over the bases in *reverse*. This is necessary in
        # order to preserve the correct order of fields.
        for base in bases[::-1]:
            if hasattr(base, 'base_fields'):
                fields = base.base_fields.items() + fields

        attrs['base_fields'] = SortedDictFromList(fields)
        return type.__new__(cls, name, bases, attrs)

class ExtensibleForm(forms.BaseForm):
    """
    Usage Notes: In the template, the form.pets variable and the 
    'add_another_pet' input must be named correctly 
    """
    # This is a separate class from BaseForm in order to abstract the way
    # self.fields is specified. This class (Form) is the one that does the
    # fancy metaclass stuff purely for the semantic sugar -- it allows one
    # to define a form using declarative syntax.
    # BaseForm itself has no way of designating self.fields.
    __metaclass__ = DeclarativeExtensibleFormMetaclass
    
    def __init__(self, *args, **kwargs):
        """  """      
        ## I think that what is happening is that the build_repeaters() call
        ## from the view (when adding more pets) is over-writing the earlier 
        ## one from this __init__...What we want is,  __init__ to automatically
        ## build itself up from *either* data *or* initial, whichever is 
        ## supplied.
        ## Then,after that, we want to add more field(s) in a non-destructive 
        ## way,not wiping over what has already been done. 
        touched_initialised_add_form  = ('initial' in kwargs) and not ('main_object' in kwargs)
        touched_initialised_edit_form  = ('initial' in kwargs) and ('main_object' in kwargs)
        touched_add_form  = args and not ('main_object' in kwargs)
        touched_edit_form = args and ('main_object' in kwargs)   
        fresh_add_form    = (not args) and not ('main_object' in kwargs)
        fresh_edit_form   = (not args) and ('main_object' in kwargs) and not ('initial' in kwargs)
         
        ## If we have been passed an owner_id, we are going to stick the 
        ## existing data for that owner_id into our fields. Stick this on as
        ## an attribute in self, so we can get to it everywhere :)
        ## Pop it rather than just getting it, to remove these keywords from the 
        ## kwargs dict or else forms.Form.__init__ will be unhappy.
       
        self.main_object = kwargs.pop('main_object', None)
        
        if fresh_edit_form:
            self.stage_type = 'fresh_edit'
            owner = self.main_object
            ## If this is an edit_form, we need to get out the existing database
            ## records and display them. Thus far we have the database ids for 
            ## the 'main entity' that this form is about (Contact, Owner, 
            ## whatever)
            ## Get out the names and values of the model fields, and put them 
            ## into our kwargs with the key 'data'. When we go through the 
            ## __init__
            ## from the superclass, these will become the data variable passed
            ## in to create a Bound form.
            model_fieldnames = [field.name for field in owner._meta.fields]
            model_fieldvalues = [owner.__getattribute__(fieldname) for 
                fieldname in model_fieldnames]      
            data_dict=dict(zip(model_fieldnames, model_fieldvalues))
            kwargs['initial'] = data_dict
        elif touched_edit_form:
            ## This is case 2
            self.stage_type = 'touched_edit'
            ## Don't need to do anything, I think - we have the data we need
            ## in  our args (from request.POST), and when we later go to save()
            ## we have a self.name_id also.
            pass
        elif fresh_add_form:
            self.stage_type = 'fresh_add'
            ## We currently do nothing for fresh_add forms - but here is a hook
            ## where we could if we wanted - ie if we wanted to change the 
            ## default values displayed or something.
            pass
        elif touched_add_form:
            self.stage_type = 'touched_add'
            ## We currently do nothing for touched_add forms - all the 
            ## necessary data is in args already, but here is a 
            ## hook where we could if we wanted.
            pass
        if touched_initialised_add_form:
            self.stage_type = 'touched_initialised_add'
        if touched_initialised_edit_form:
            self.stage_type = 'touched_initialised_edit'
        ## Now we are ready to call BaseForm.__init__    
        forms.BaseForm.__init__(self, *args, **kwargs)
        self.build_repeaters()
       
        
    def put_fieldlists_into_form_fields(self):    
        ## First build-up the fieldlists inside the field-sequences.
        ##
        ## 'fs' will be a tuple of (name, FieldList) ie
        ##
        ## ('pets', 
        ## <wjContact.contacts.views.fields.FieldList object at 0x016218B0>) 
        ##             
        ## base_name is the 'root name' for all the fields in this 
        ## field-list, ie phone for phones, pet for pets, and so on  
        ## So how_many tells us how many fields there need to be in
        ## this field-list to hold the info for the entries which
        ## already exist in the form-data (or in form.initial).              
#        for fs in self.fieldlists:
#                how_many=len(self.get_names_from_datadict(fs[1].base_name))         
        ## And then deal with getting them all into self.fields    
        all_fields_from_fieldlists = []
        for fl in self.fieldlists:  
            ## Now set up the *individual* fields that should end up in
            ## self.fields.       
            all_fields_from_fieldlists.extend(fl[1].get_names_and_fields())
        ## all_fields_from_sequences is now a list of pairs of (key, value),
        ## but we need a dict:
        all_fields_from_fieldlists_as_dict = dict(all_fields_from_fieldlists)
        ## update self.fields. The update() method over-writes existing entries
        ## with the same key, but this shouldn't be a problem since we have
        ## given all of the new fields a numerical suffix, ie pet_0, pet_1
        ## Also, we never should have tried to have another field named 'pet' 
        ## anyway!
        self.fields.update(all_fields_from_fieldlists_as_dict)
            
    def build_repeaters(self):
        if self.stage_type == 'fresh_add': 
            """Can combine this with one of the other conditionals """ 
            ## first build-up the fieldlists inside the field-sequences
            for dummy, fieldlist in self.fieldlists:
                ## fs will be a tuple of (name, FieldList) ie
                ## ('pets', 
                ## <wjContact.contacts.views.fields.FieldList object at 0x016218B0>)
                fieldlist.build_fields(number=1)
                ## Also give each one a reference to the formn  they are on, so
                ## that when they are needing to Bind, they can
                fieldlist.set_form(self)   
            ## And then deal with getting them all into self.fields 
            self.put_fieldlists_into_form_fields()      

        if  (self.stage_type == 'touched_initialised_edit') or (self.stage_type
             == 'touched_initialised_add'):
            """Can combine this with one of the other conditionals """ 
            ## first build-up the fieldlists inside the field-sequences         
            for dummy, fieldlist in self.fieldlists:
                ## fs will be a tuple of (name, FieldList) ie
                ## ('pets', 
                ## <wjContact.contacts.views.fields.FieldList object at 0x016218B0>)              
                ## base_name is the 'base name' for this recurring field, ie 
                ## phone for phones, pet for pets, etc                
                how_many=len(self.get_names_from_datadict(fieldlist.base_name))
                fieldlist.build_fields(how_many)
                ## Also give each one a reference to the form  they are on, so
                ## that when they are needing to Bind, they can do so.
                fieldlist.set_form(self)  
               
            ## And then deal with getting them all into self.fields 
            self.put_fieldlists_into_form_fields()       
            
        if (self.stage_type == 'touched_add') or (self.stage_type == 
            'touched_edit'): 
            ## first build-up the fieldlists inside the field-sequences
            for dummy, fieldlist in self.fieldlists:
                ## fs will be a tuple of (name, FieldList) ie
                ## ('pets', 
                ## <wjContact.contacts.views.fields.FieldList object at 0x016218B0>)              
                ## base_name is the 'base name' for this recurring field, ie 
                ## phone for phones, pet for pets, etc                
                how_many=len(self.get_names_from_datadict(fieldlist.base_name))
                fieldlist.build_fields(how_many)
                ## Also give each one a reference to the form  they are on, so
                ## that when they are needing to Bind, they can do so.
                fieldlist.set_form(self)  

            ## And then deal with getting them all into self.fields  
            self.put_fieldlists_into_form_fields()      
           
        if self.stage_type == 'fresh_edit': 
            """Subclasses will implement """
            """ Or if not using subclasses to implement, try using
                form.initial as a whole rather than populating the individual 
                fields perhaps?"""
            ## If we are 'fresh', we need to get the relevant data out of the 
            ## database, then build the appropriate 'field' object for each 
            ## one.
            ## First get the needed data out of the database
            ## Then build-up the fieldlists inside the field-sequences
            fields=[]
            for dummy, fieldlist in self.fieldlists:
                fk_fieldname = fieldlist.fk_fieldname
                model = fieldlist.model_class
                kwargs={fk_fieldname+'__exact': self.main_object}
                bits=model.objects.filter(**kwargs)
                
                for i, bit in enumerate(bits): 
                    for attr in fieldlist.template_field.widget.database_to_widget.keys():
                        value = bit.__getattribute__(attr)            
                        fieldname = fieldlist.base_name + '_%s' % (i+1)     
                        value_fieldname=fieldlist.template_field.widget.widget_to_database[fieldlist.template_field.widget.database_to_widget[attr]]
                        self.initial[value_fieldname % fieldname] = value                              

                how_many=len(self.get_names_from_datadict(fieldlist.base_name))
                fieldlist.build_fields(how_many)
                ## Also give each one a reference to the form  they are on, so
                ## that when they are needing to Bind, they can do so.
                fieldlist.set_form(self) 
                pluralised_fieldname = fieldlist.base_name + 's'
                self.__setattr__(pluralised_fieldname, fieldlist)        
            ## And then deal with getting them all into self.fields  
            self.put_fieldlists_into_form_fields()      
           

    def get_names_from_datadict(self, base_name):           
        """
        Return a list of the actual individual fieldnames by which all of the
        fields which are part of a field_array ... are known by in 
        self.fields by looking in self.data (or in self.initial)
        
        This will be something like 
        ['phone_1', 'phone_2'...'phone_N']
        
        """
        from sets import Set
        numbered_fields=[]    
        if self.data:
            data = self.data
        if self.initial:
            data = self.initial
            
        for name in data.keys():
            split_name = name.split('_')
            if (split_name[0] == base_name):
                fieldname = '_'.join( [split_name[0], split_name[1] ])
                numbered_fields.append(fieldname)       
        numbered_fields = Set(numbered_fields)
        ## Now phones should be something like just:
        ## ['repeating_phone_1', 'repeating_phone_2'] 
        return numbered_fields   
           
    def save_fieldlists(self): 
        for fieldlist in self.fieldlists:
            ## fieldlist wil be a tuple of (name, FieldList) ie
            ##('pets', <wjContact.contacts.views.fields.FieldList object at 0x0161CA50>)
            for field in self.__getattribute__(fieldlist[0]):
                ## items in self.pets are BoundFields - go inside them to
                ## access the actual original 'field'
                ## <wsl> FIXME
                if 'main_object' in self.__dict__:              
                    main_object_id = self.main_object.id
                else:
                    main_object_id = None
                field.field.widget.save_to_database(self.data, main_object_id, field.name)

